#include "face.h"
#include <iostream>
#include <cmath>

#include <GL/gl.h>

#define ERROR 0.00001

Face::Face(int _id, Object *_o, bool _b) : id(_id), inBoundary(_b), object(_o){
    color = Color(1.0, 1.0, 1.0, 0.5);
    selected = false;
    normalCalculated = false;

    edge = NULL;
    edgeSignal = true;
}

//Face::Face(int _id, Vertex *_v1, Vertex *_v2, Vertex *_v3) : id(_id){
//    color = Color(1.0, 0.0, 1.0, 0.5);
//    selected = false;
//    inBoundary = false;
//    normalCalculated = false;
//
//    object = NULL;
//    edge = NULL;
//    edgeSignal = true;
//
//    verticesInThePlan.push_back(_v1);
//    verticesInThePlan.push_back(_v2);
//    verticesInThePlan.push_back(_v3);
//
//    edge = new
//
//}

Face::Face(int _id, Object *_o, Edge *_e, bool _es, bool _b) : id(_id), inBoundary(_b), object(_o), edge(_e), edgeSignal(_es){
    color = Color(1.0, 1.0, 1.0, 0.5);
    selected = false;
    normalCalculated = false;
}

Face::Face(int _id, Object *_o, Edge *_e, bool _es, Color _c, bool _b) : id(_id), inBoundary(_b), color(_c), object(_o), edge(_e), edgeSignal(_es){
    selected = false;
    normalCalculated = false;
}

Face::~Face(){
}

int Face::getID() const{
    return id;
}

void Face::select(bool _s){
    selected = _s;
}

bool Face::isSelected(){
    return selected;
}

void Face::setIsBoundary(bool _b){
    inBoundary = _b;
}

bool Face::isBoundary(){
    return inBoundary;
}

void Face::setColor(Color _c){
    color = _c;
}

Color Face::getColor(){
    return color;
}

void Face::setObject(Object *_o){
    object = _o;
}

Object *Face::getObject(){
    return object;
}

void Face::setEdge(Edge *_e, bool _es){
    edge = _e;
    edgeSignal = _es;
}

Edge *Face::getEdge(){
    return edge;
}

bool Face::getEdgeSignal(){
    return edgeSignal;
}

void Face::addVertexInThePlan(Vertex *_v){
    verticesInThePlan.push_back(_v);
}

void Face::setVerticesInThePlan(std::vector<Vertex *> _v){
    verticesInThePlan = _v;
}

std::vector<Vertex *> Face::getVerticesInThePlan(){
    return verticesInThePlan;
}

Vertex *Face::getVertexInThePlan(int _i){
    return verticesInThePlan.at(_i);
}

unsigned int Face::getVerticesInThePlanSize(){
    return verticesInThePlan.size();
}

void Face::print(){
    Point n = normal();
    char signal;
    if(!edgeSignal) signal = '-';
    else signal = '+';
	std::cout << "F" << id;
	if(edge) std::cout << ";\t" << signal <<  "E" << edge->getID();
	else std::cout << ";\t";
	if(inBoundary) std::cout << ";\tÉ da borda!";
	std::cout << ";\tnormal: ";
	n.print();
}

Point Face::normal(){
    if(inBoundary){
        if(!normalCalculated){
            //rodando para achar duas aresta consecutivas qeu não são paralelas
            Edge *next;
            bool nextSignal;

            if(edge->getClockwise() && (edge->getClockwise()->getID() == id)){
                next = edge->getNextClockwise();
                nextSignal = edge->getNextClockwiseSignal();
            }else{
                next = edge->getNextCounterClockwise();
                nextSignal = edge->getNextCounterClockwiseSignal();
            }

            while(isParallel(*edge, *next)){
    //            std::cout << "è paralelo!" << std::endl;
                edge = next;
                edgeSignal = nextSignal;

                if(edgeSignal && edge->getClockwise()){
                    next = edge->getNextClockwise();
                    nextSignal = edge->getNextClockwiseSignal();
                }else{
                    next = edge->getNextCounterClockwise();
                    nextSignal = edge->getNextCounterClockwiseSignal();
                }
            }

            calculateNormal();

            normalCalculated = true;
        }
    }else{
        if(!normalCalculated) calculateNormalByVertices();

        normalCalculated = true;
    }

    return normalVector;
}

void Face::calculateNormal(){
    Edge *next;
    //counter clockwise
    if(!edgeSignal){

        next = edge->getNextCounterClockwise();
//        std::cout <<"[CCW] A face " << id << " esta usando a aresta " << edge->getID() << " e a proxima " << next->getID()<< " para calcular a normal." <<std::endl;


        if( (edgeSignal && edge->getNextCounterClockwiseSignal()) || (!edgeSignal && !edge->getNextCounterClockwiseSignal()) ){
            normalVector = ((edge->getEnd()->getPoint() - edge->getStart()->getPoint()).crossProduct(next->getEnd()->getPoint() - next->getStart()->getPoint())).normalization();
        }
        else{
            normalVector = ((next->getEnd()->getPoint() - next->getStart()->getPoint()).crossProduct(edge->getEnd()->getPoint() - edge->getStart()->getPoint())).normalization();
        }
    }
    //clockwise
    else{

        next = edge->getNextClockwise();
//        std::cout <<"[CW] A face " << id << " esta usando a aresta " << edge->getID() << " e a proxima " << next->getID()<< " para calcular a normal." <<std::endl;


        if( (edgeSignal && edge->getNextClockwiseSignal()) || (!edgeSignal && !edge->getNextClockwiseSignal()) ){
            normalVector = ((edge->getEnd()->getPoint() - edge->getStart()->getPoint()).crossProduct(next->getEnd()->getPoint() - next->getStart()->getPoint())).normalization();
        }
        else{
            normalVector = ((next->getEnd()->getPoint() - next->getStart()->getPoint()).crossProduct(edge->getEnd()->getPoint() - edge->getStart()->getPoint())).normalization();
        }
    }
}

void Face::calculateNormalByVertices(){
    Point a = verticesInThePlan.at(0)->getPoint();
    Point b = verticesInThePlan.at(1)->getPoint();
    Point c = verticesInThePlan.at(2)->getPoint();

    normalVector = (b-a).crossProduct(c-a).normalization();
}


bool haveIntersection(Point _v0, Point _v1, Point _v2, Point _u0, Point _u1, Point _u2){
    bool shareOne = false;
    bool twoInSameSideU = false;
    bool twoInSameSideV = false;
    bool twoWith0U = false;
    bool twoWith0V = false;
    int whoIs0U = -1;
    int whoIs0V = -1;

    if((_v0 == _u0 || _v0 == _u1 || _v0 == _u2)){
        shareOne = true;
    }
    if((_v1 == _u0 || _v1 == _u1 || _v1 == _u2)){
        if (!shareOne) shareOne = true;
    }
    if((_v2 == _u0 || _v2 == _u1 || _v2 == _u2)){
        if (!shareOne) shareOne = true;
    }


    Point n1, n2; //normais
    Point e1, e2; //vetores das arestas
    double d1, d2; //parte da equação
    double du0, du1, du2, dv0, dv1, dv2; //distancia dos pontos aos planos

    //descobrindo o plano do triangulo 1
    e1 = _v1 - _v0;
    e2 = _v2 - _v0;
    n1 = e1.crossProduct(e2);
    d1 = -n1.dotProduct(_v0);
    //plano 1 => n1.X + d1 = 0

    //calculo da distancias dos pontos ao plano
    du0 = n1.dotProduct(_u0) + d1;
    du1 = n1.dotProduct(_u1) + d1;
    du2 = n1.dotProduct(_u2) + d1;

//    std::cout <<"as distancias do primeiro triangulo ao segundo: " << du0 << ", " << du1 << ", " << du2 << "." <<std::endl;

    //se for igual a zero é pq estão no mesmo plano
    if(fabs(du0) < ERROR) du0 = 0.0;
    if(fabs(du1) < ERROR) du1 = 0.0;
    if(fabs(du2) < ERROR) du2 = 0.0;

    //se tiverem todos de um mesmo lado de um plano, não houve interseção
//    if(testSignal(du0, du1, du2)) return false;

    //testando se estiverem no mesmo plano
    if(fabs(du0) < ERROR){
        //d0 = 0
        if(fabs(du1) < ERROR){
            //d0 = 0, d1 = 0
            if(fabs(du2) < ERROR){//todos iguais a zero
                //d0 = 0, d1 = 0, d2 = 0
                //estão no mesmo plano
                if(haveIntersection(_v0, _v1, _u0, _u1)) return true;
                if(haveIntersection(_v0, _v1, _u1, _u2)) return true;
                if(haveIntersection(_v0, _v1, _u2, _u0)) return true;

                if(haveIntersection(_v1, _v2, _u0, _u1)) return true;
                if(haveIntersection(_v1, _v2, _u1, _u2)) return true;
                if(haveIntersection(_v1, _v2, _u2, _u0)) return true;

                if(haveIntersection(_v2, _v0, _u0, _u1)) return true;
                if(haveIntersection(_v2, _v0, _u1, _u2)) return true;
                if(haveIntersection(_v2, _v0, _u2, _u0)) return true;
                return false;
            }else{
                //d0 = 0, d1 = 0, d2 != 0
                twoWith0U = true;
                if((_u0 == _v0 || _u0 == _v1 || _u0 == _v2) && (_u1 == _v0 || _u1 == _v1 || _u1 == _v2))
                    return false;
            }
        }else{
            //d0 = 0, d1 != 0
            if(fabs(du2) < ERROR){
                //d0 = 0, d1 != 0, d2 = 0
                twoWith0U = true;
                if((_u0 == _v0 || _u0 == _v1 || _u0 == _v2) && (_u2 == _v0 || _u2 == _v1 || _u2 == _v2))
                    return false;
            }
            else{
                //d0 = 0, d1 != 0, d2 != 0
                if(du1*du2 > 0.0){
                    if(_u0 == _v0 || _u0 == _v1 || _u0 == _v2) return false;//um sendo zero e os outros dois sendo do mesmo lado não ha interseção
                    twoInSameSideU = true;
                    whoIs0U = 0;
                }
            }
        }
    }else{
        //d0 != 0
        if(fabs(du1) < ERROR){
            //d0 != 0, d1 = 0
            if(fabs(du2) < ERROR){
                //d0 != 0, d1 = 0, d2 = 0
                twoWith0U = true;
                if((_u1 == _v0 || _u1 == _v1 || _u1 == _v2) && (_u2 == _v0 || _u2 == _v1 || _u2 == _v2))
                    return false;
            }
            else{
                //d0 != 0, d1 = 0, d2 != 0
                if(du0*du2 > 0.0){
                    if(_u1 == _v0 || _u1 == _v1 || _u1 == _v2) return false;
                    twoInSameSideU = true;
                    whoIs0U = 1;
                }
            }
        }else{
            //d0 != 0, d1 != 0
            if(fabs(du2) < ERROR){
                //d0 != 0, d1 != 0, d2 = 0
                if(du0*du1 > 0.0){
                    if(_u2 == _v0 || _u2 == _v1 || _u2 == _v2) return false;
                    twoInSameSideU = true;
                    whoIs0U = 2;
                }
            }
            else{
                if((du0*du1 > 0.0) && (du0*du2 > 0.0)) return false;//um sendo zero e os outros dois sendo do mesmo lado não ha interseção
            }
        }
    }

    //----fazendo mesma coisa com o outro triangulo
    //descobrindo o plano do triangulo 2
    e1 = _u1 - _u0;
    e2 = _u2 - _u0;
    n2 = e1.crossProduct(e2);
    d2 = -n2.dotProduct(_u0);
    //plano 2 => n2.X + d2 = 0

    //calculo da distancias dos pontos ao plano
    dv0 = n2.dotProduct(_v0) + d2;
    dv1 = n2.dotProduct(_v1) + d2;
    dv2 = n2.dotProduct(_v2) + d2;

    //se for igual a zero é pq estão no mesmo plano
    if(fabs(dv0) < ERROR) dv0 = 0.0;
    if(fabs(dv1) < ERROR) dv1 = 0.0;
    if(fabs(dv2) < ERROR) dv2 = 0.0;

    //se tiverem todos de um mesmo lado de um plano, não houve interseção
//    if(testSignal(dv0, dv1, dv2)) return false;

    if(fabs(dv0) < ERROR){
        //d0 = 0
        if(fabs(dv1) < ERROR){
            //d0 = 0, d1 = 0
            if(fabs(dv2) < ERROR){//todos iguais a zero
                //d0 = 0, d1 = 0, d2 = 0
                //estão no mesmo plano
                if(haveIntersection(_v0, _v1, _u0, _u1)) return true;
                if(haveIntersection(_v0, _v1, _u1, _u2)) return true;
                if(haveIntersection(_v0, _v1, _u2, _u0)) return true;

                if(haveIntersection(_v1, _v2, _u0, _u1)) return true;
                if(haveIntersection(_v1, _v2, _u1, _u2)) return true;
                if(haveIntersection(_v1, _v2, _u2, _u0)) return true;

                if(haveIntersection(_v2, _v0, _u0, _u1)) return true;
                if(haveIntersection(_v2, _v0, _u1, _u2)) return true;
                if(haveIntersection(_v2, _v0, _u2, _u0)) return true;
                return false;
            }else{
                //d0 = 0, d1 = 0, d2 != 0
                twoWith0V = true;
                if((_v0 == _u0 || _v0 == _u1 || _v0 == _u2) && (_v1 == _u0 || _v1 == _u1 || _v1 == _u2))
                    return false;
            }
        }else{
            //d0 = 0, d1 != 0
            if(fabs(dv2) < ERROR){
                //d0 = 0, d1 != 0, d2 = 0
                twoWith0V = true;
                if((_u0 == _u0 || _v0 == _u1 || _v0 == _u2) && (_v2 == _u0 || _v2 == _u1 || _v2 == _u2))
                    return false;
            }
            else{
                //d0 = 0, d1 != 0, d2 != 0
                if(dv1*dv2 > 0.0){
                    if(_v0 == _u0 || _v0 == _u1 || _v0 == _u2) return false;//um sendo zero e os outros dois sendo do mesmo lado não ha interseção
                    twoInSameSideV = true;
                    whoIs0V = 0;
                }
            }
        }
    }else{
        //d0 != 0
        if(fabs(dv1) < ERROR){
            //d0 != 0, d1 = 0
            if(fabs(dv2) < ERROR){
                //d0 != 0, d1 = 0, d2 = 0
                twoWith0V = true;
                if((_v1 == _u0 || _v1 == _u1 || _v1 == _u2) && (_v2 == _u0 || _v2 == _u1 || _v2 == _u2))
                    return false;
            }
            else{//d0 != 0, d1 = 0, d2 != 0
                if(dv0*dv2 > 0.0){
                    if(_v1 == _u0 || _v1 == _u1 || _v1 == _u2) return false;
                    twoInSameSideV = true;
                    whoIs0V = 1;
                }
            }
        }else{
            //d0 != 0, d1 != 0
            if(fabs(dv2) < ERROR){
                //d0 != 0, d1 != 0, d2 = 0
                if(dv0*dv1 > 0.0){
                    if(_v2 == _u0 || _v2 == _u1 || _v2 == _u2) return false;
                    twoInSameSideV = true;
                    whoIs0V = 2;
                }
            }
            else{//somente um igual a zero
                if((dv0*dv1 > 0.0) && (dv0*dv2 > 0.0)) return false;//um sendo zero e os outros dois sendo do mesmo lado não ha interseção
            }
        }
    }

//    //calculo da direção da linha de interseção
//    Point direction = n1.crossProduct(n2);
//    unsigned int index = 0;
//    double max = fabs(direction.x), auxY = fabs(direction.y), auxZ = fabs(direction.z);
//    double vp0, vp1, vp2, up0, up1, up2;
//
//    //descobrindo o maior componente de D
//    if(auxY > max){
//        max = auxY;
//        index = 1;
//    }
//    if(auxZ > max){
//        max = auxZ;
//        index = 2;
//    }
//
//    //fazendo uma projeção simplificada em L
//    vp0 = _v0.intoVector()[index];
//    vp1 = _v1.intoVector()[index];
//    vp2 = _v2.intoVector()[index];
//
//    up0 = _u0.intoVector()[index];
//    up1 = _u1.intoVector()[index];
//    up2 = _u2.intoVector()[index];


    //calculo da direção da linha de interseção
    Point direction = (n1.normalization()).crossProduct(n2.normalization());
    double n1n2 = n1.dotProduct(n2);
    double n1n1 = n1.dotProduct(n1);
    double n2n2 = n2.dotProduct(n2);

    double s1 = -d1;
    double s2 = -d2;

    double div = ((n1n2 * n1n2) - (n1n1 * n2n2));

    double a = ((s2*n1n2) - (s1*n2n2)) / div;
    double b = ((s1*n1n2) - (s2*n1n1)) / div;

    Point p = (n1 * a) + (n2 * b);
    //equação da linha: L = P + t*(n1 × n2) = (a*n1 + b*n2) + t*(n1 × n2)
//
//_u0.print();
//_u1.print();
//_u2.print();
//_v0.print();
//_v1.print();
//_v2.print();
//std::cout <<"!!!!!!!!!!! num retornou ate aqui" <<std::endl;
//std::cout <<"P: "; p.print();
//std::cout <<"D: "; direction.print();
    glBegin(GL_LINE);

        glLineWidth(3);
        glColor3f(1.0, 1.0, 1.0);

        glVertex3dv(p.intoVector());
        glVertex3dv((p+direction.normalization()).intoVector());

        glLineWidth(1);
    glEnd();


    double daux;
    Point aux;
    //rotacionando os pontos para deixar os dois primeiros de um lado só
    if(dv0*dv1 < 0){ //se estiverem de lados opostos
        //checa em qual lado tah o outro ponto
        if(dv0*dv2 < 0){
            aux = _v0;
            daux = dv0;

            _v0 = _v1;
            dv0 = dv1;

            _v1 = _v2;
            dv1 = dv2;

            _v2 = aux;
            dv2 = daux;
        }else{
            aux = _v0;
            daux = dv0;

            _v0 = _v2;
            dv0 = dv2;

            _v2 = _v1;
            dv2 = dv1;

            _v1 = aux;
            dv1 = daux;

//std::cout <<"trocando V2" <<std::endl;
//            if(whoIs0V == 0) whoIs0V = 2;
//            else if(whoIs0V == 1) whoIs0V = 0;
//            else if(whoIs0V == 2) whoIs0V = 1;
        }
    }

    if(twoInSameSideV){
        //rotacionar para que seja o 2
        if(whoIs0V == 0){
            aux = _v0;
            daux = dv0;

            _v0 = _v1;
            dv0 = dv1;

            _v1 = _v2;
            dv1 = dv2;

            _v2 = aux;
            dv2 = daux;
        }
        else if(whoIs0V == 1){
            aux = _v0;
            daux = dv0;

            _v0 = _v2;
            dv0 = dv2;

            _v2 = _v1;
            dv2 = dv1;

            _v1 = aux;
            dv1 = daux;
        }
    }



    if(du0*du1 < 0){ //se estiverem de lados opostos
        //checa em qual lado tah o outro ponto
        if(du0*du2 < 0){
            aux = _u0;
            daux = du0;

            _u0 = _u1;
            du0 = du1;

            _u1 = _u2;
            du1 = du2;

            _u2 = aux;
            du2 = daux;
//std::cout <<"trocando U1" <<std::endl;
//            if(whoIs0U == 0) whoIs0U = 1;
//            else if(whoIs0U == 1) whoIs0U = 2;
//            else if(whoIs0U == 2) whoIs0U = 0;
        }else{
            aux = _u0;
            daux = du0;

            _u0 = _u2;
            du0 = du2;

            _u2 = _u1;
            du2 = du1;

            _u1 = aux;
            du1 = daux;

//std::cout <<"trocando U2" <<std::endl;
//            if(whoIs0U == 0) whoIs0U = 2;
//            else if(whoIs0U == 1) whoIs0U = 0;
//            else if(whoIs0U == 2) whoIs0U = 1;
        }
    }
    if(twoInSameSideU){
        //rotacionar para que seja o 2
        if(whoIs0U == 0){
            aux = _u0;
            daux = du0;

            _u0 = _u1;
            du0 = du1;

            _u1 = _u2;
            du1 = du2;

            _u2 = aux;
            du2 = daux;
        }
        else if(whoIs0U == 1){
            aux = _u0;
            daux = du0;

            _u0 = _u2;
            du0 = du2;

            _u2 = _u1;
            du2 = du1;

            _u1 = aux;
            du1 = daux;
        }
    }

    //calculando onde cada triangulo encosta na reta
    double vp0, vp1, vp2, up0, up1, up2;
    double tv0, tv1, tu0, tu1;

    vp0 = direction.dotProduct(_v0 - p);
    vp1 = direction.dotProduct(_v1 - p);
    vp2 = direction.dotProduct(_v2 - p);

    up0 = direction.dotProduct(_u0 - p);
    up1 = direction.dotProduct(_u1 - p);
    up2 = direction.dotProduct(_u2 - p);

//        if(whoIs0V == 0) whoIs0V = 2;
//        else if(whoIs0V == 1) whoIs0V = 0;
//        else if(whoIs0V == 2) whoIs0V = 1;
        tv0 = vp0 + ((vp2 - vp0)*(dv0/(dv0 - dv2)));
        tv1 = vp1 + ((vp2 - vp1)*(dv1/(dv1 - dv2)));
//    }

    tu0 = up0 + ((up2 - up0)*(du0/(du0 - du2)));
    tu1 = up1 + ((up2 - up1)*(du1/(du1 - du2)));

std::cout <<"dv0: " <<dv0  << "\tdv1: " <<dv1<< "\tdv2: " <<dv2 <<std::endl;
std::cout <<"du0: " <<du0  << "\tdu1: " <<du1<< "\tdu2: " <<du2 <<std::endl;
std::cout <<"dv0 - dv2: " <<dv0 - dv2 << "\tdv1 - dv2: " <<dv1 - dv2 <<std::endl;
std::cout <<"du0 - du2: " <<du0 - du2 << "\tdu1 - du2: " <<du1 - du2 <<std::endl;
std::cout <<"!!!!!!!!!!! num retornou ate aqui \ttv0: " << tv0 << "\ttu0: " <<tu0 << "\ttv1: " <<tv1 << "\ttu1: " <<tu1 <<std::endl;



//std::cout <<"!!!!!!!!!!! continuou sme retornar!!! " << twoWith0U << twoWith0V <<std::endl;
//    //testando quantas interseções são
//    if(shareOne && !twoWith0U && !twoWith0V){
//        if(tv0 == tu0 && tv1 != tu1) return false;
//        if(tv1 == tu0 && tv0 != tu1) return false;
//        if(tv0 == tu1 && tv1 != tu0) return false;
//        if(tv1 == tu1 && tv0 != tu0) return false;
//    }
    if(shareOne){
//        if(!twoWith0U && !twoWith0V){
//            if(tv0 == tu0 && tv1 != tu1) return false;
//            if(tv1 == tu0 && tv0 != tu1) return false;
//std::cout <<"!!!!!!!!!!! continuou sme retornar!!! " << twoWith0U << twoWith0V <<std::endl;
//            if(tv0 == tu1 && tv1 != tu0) return false;
//            if(tv1 == tu1 && tv0 != tu0) return false;
//        }else{
            if(tv0 == tu0 && ((tv1 >0 && tu1 < 0) ||(tv1 <0 && tu1 > 0)))  return false;
            if(tv0 == tu1 && ((tv1 >0 && tu0 < 0) ||(tv1 <0 && tu0 > 0)))  return false;
            if(tv1 == tu0 && ((tv0 >0 && tu1 < 0) ||(tv0 <0 && tu1 > 0)))  return false;
            if(tv1 == tu1 && ((tv0 >0 && tu0 < 0) ||(tv0 <0 && tu0 > 0)))  return false;
//        }
    }


//    se forem seguidos
//pro lado positivo
    if(tv0 <= tu0 && tu0 <= tv1) return true;
    if(tv0 <= tu1 && tu1 <= tv1) return true;
    if(tu0 <= tv0 && tv0 <= tu1) return true;
    if(tu0 <= tv1 && tv1 <= tu1) return true;

//pro lado egativo
    if(tv1 <= tu0 && tu0 <= tv0) return true;
    if(tv1 <= tu1 && tu1 <= tv0) return true;
    if(tu1 <= tv0 && tv0 <= tu0) return true;
    if(tu1 <= tv1 && tv1 <= tu0) return true;






    //lembrar de aceitar akeles que so se tocam na mesma aresta

    //se ocorrer alguma interseção pode retornar true
    return false;//?????????????????????????
}

//bool testSignal(double d0, double d1, double d2){
//    if()
//    if(d0*d1 > 0.0 && d0*d2 > 0.0) return false;
//}
